package main;

import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Observable;
import java.util.Observer;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

public class RunnableExperiment extends Observable implements Runnable{

	private Date startTime;
	private Expirement expirement;
	private Repository repository;
	//private ExperimentObserver manager;
	private int worktodo;
	private double deadline;
	private HeadOfLaboratory head;
	
	public RunnableExperiment(Expirement e,Repository r, HeadOfLaboratory h , Observer o)
	{
		this.expirement=e;
		expirement.setStatus(2);
		head=h;
		this.repository=r;
		//startTime=new Date();
		//this.manager=c;
		worktodo=e.getRuntime();
		deadline=worktodo*1.15;
		addObserver(o);
		
	}


	public void performExpNADAV() throws InterruptedException
	{

		startTime= new Date();
		
		long sleepTime=0;
		System.out.println("exp "+expirement.getID()+ " started at "+startTime.getTime());
		while (worktodo>0)
		{
			System.out.println("exp "+expirement.getID()+" got "+worktodo + " hours to work");
			boolean hasTakeEquipment=true;
			Semaphore missing = repository.takeEquipment(expirement.getReqEquipment());
			if(missing!=null)
			{	
				long hoursWorked=((new Date().getTime()) - startTime.getTime())*100;
				double hoursToSpare=deadline-hoursWorked;
				long timeToWait=((long)(hoursToSpare*0.8));
				hasTakeEquipment=missing.tryAcquire(timeToWait*100,TimeUnit.MILLISECONDS);
			}
			if(hasTakeEquipment)
			{	
				if(worktodo>=8)
				{
					Thread.sleep(800);
					worktodo=worktodo-8;
				}
				else
				{	
					Thread.sleep(worktodo*100);
					worktodo=0;
				}
				repository.addEquipment(expirement.getReqEquipment());
				Thread.sleep(1600);
				sleepTime=sleepTime+1600;
			}
			else
			{
				System.out.println(new Date().getTime()+" "+expirement.getID() + "need equipment");
				notifyEquipmentNeeded();
				hasTakeEquipment=missing.tryAcquire();	
			}
		}
		notifyComplete(startTime,sleepTime);
	}


	public void performExp() throws InterruptedException
	{
		long runtime=0;
		startTime = new Date();
		Date workTime ;
		System.out.println("exp "+expirement.getID()+ " started at "+startTime.getTime());
		HashMap<String, Integer> hashmap = expirement.getReqEquipment();
		Iterator<String> it =null;
		String name = null;
		boolean gotEquipment = true;
		while (worktodo>0)
		{
			workTime=new Date();
			System.out.println(gotEquipment+"exp "+expirement.getID()+" got "+worktodo + " hours to work");
			if(gotEquipment)
			{
				if(hashmap!=null)
					it=hashmap.keySet().iterator();
				if(it.hasNext())
					name=it.next();
			}
			else
				gotEquipment=true;
			while(it!=null && gotEquipment)
			{	
				System.out.println("looking for " + hashmap.get(name)+ " "+name);
				Semaphore sem =repository.takeEquipment(name);
				if(sem!=null)
				{	
					System.out.println(sem.availablePermits()+" " +name + " found");
					gotEquipment=sem.tryAcquire(hashmap.get(name),100,TimeUnit.MILLISECONDS);
				}	
				else
				{
					gotEquipment=false;
					name= "fuck";
				}
				System.out.println(name+" " + hashmap.get(name)+ " "+gotEquipment);

				if(gotEquipment && it.hasNext())
					name=it.next();
				else
				{
					if(gotEquipment)
						it=null;
				}
					

			}

			if(gotEquipment)
			{

				if(worktodo>=8)
				{

					Thread.sleep(800);
					worktodo=worktodo-8;
				}
				else
				{
					Thread.sleep(worktodo*100);
				worktodo=0;
				}
				
				repository.addEquipment(hashmap);
				runtime=runtime+(new Date().getTime()-workTime.getTime())/100;
				System.out.println("runtime/todo: " +runtime+"/"+worktodo);
				Thread.sleep(1600);
			}
			else
			{
				runtime=runtime+(new Date().getTime()-workTime.getTime())/100;
				//System.out.println("exp "+expirement.getID()+ "waiting for item");
				System.out.println(expirement.getID() + " waiting for " +runtime);
				
				if(runtime+worktodo>deadline*0.95)
				{
					System.out.println("exp "+expirement.getID()+" waiting with " +worktodo+ " hours to work and has only "+(deadline-runtime)+" hours to finish! (runtime: "+runtime+" work left:"+worktodo+" deadline: "+deadline+")");
					notifyEquipmentNeeded();
				}
			}
		
		}
		notifyComplete(runtime);
	}

	/**
 	* notify observer because experiment done
 	* @param runtime - true real runtime of experiment
 	*/
	private void notifyComplete(long runtime) {
		expirement.updateRealtimeRunning(runtime);
		System.out.println("exp "+expirement.getID()+" FINISHED after :"+(runtime)+" HOURS");
		expirement.setStatus(3);
		setChanged();
		notifyObservers(this.expirement);
	}

	/**
	 * notify observer because experiment done
	 * @param startTime - time experiment started
	 * @param sleepTime - night time for sleep - doesnt count as experiment time 
	 */
	private void notifyComplete(Date startTime,long sleepTime) {
		Date endTime=new Date();
		long experimentTime=endTime.getTime()-startTime.getTime()-sleepTime;
		System.out.println("exp "+expirement.getID()+" FINISHED after :"+(experimentTime/100)+" HOURS");
		expirement.setStatus(3);
		setChanged();
		notifyObservers(this.expirement);	
	}
	
	public void run(){	
		try {
			performExp();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

private double rewardExperiment(long experimentTime) {
		if(deadline >= experimentTime/100)	//we managed to stand at reqvaried time
		{
			System.out.println("exp "+expirement.getID()+" was rewarded FULLY:"+expirement.getReward());
			return expirement.getReward();
		}
		else
		{
			System.out.println("exp "+expirement.getID()+" was rewarded 10%:"+expirement.getReward()*0.10);
			return expirement.getReward()*0.10; 
		}
	}

/**
 * notify observer because equipment is needed
 */
	private void notifyEquipmentNeeded() {
		expirement.setStatus(4);
		System.out.println("exp "+expirement.getID()+" need equipment - notify observer");
		setChanged();
		notifyObservers(this.expirement);
	}

}
